package top.chmin.project.cloud.common.entity;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import top.chmin.project.cloud.common.tool.LocalTool;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * chmin
 * 2023/12/15 9:35
 * cloud-builder
 */
@Slf4j
@Getter
@Setter
@Accessors(chain = true)
public abstract class PreCondition {

    protected String type = "SELECT";

    protected PreCondition parent;

    protected List<String> updateSqlList = new ArrayList<>();
    protected List<Object> updateValueList = new ArrayList<>();

    protected List<String> sqlList = new ArrayList<>();
    protected List<Object> valueList = new ArrayList<>();

    protected List<String> groupList = new ArrayList<>();
    protected List<String> orderList = new ArrayList<>();

    protected List<String> selectList = new ArrayList<>();

    protected Integer limit = null;

    protected String sql;

    public PreCondition() {
    }

    public PreCondition(PreCondition parent) {
        this.parent = parent;
    }

    public void finish(boolean and) {
        sql = String.join(and ? "\nAND\n    " : "\nOR\n    ", sqlList).replace("AND\n    (\n", "AND (\n");
        if (parent != null) {
            parent.getSqlList().add("(%s)".formatted(sql));
            parent.getValueList().addAll(valueList);
        }
    }

    public <E extends Model> Condition toCondition(Class<E> clazz, String tableName) {
        if (StringUtils.isEmpty(sql)) {
            finish(true);
        }
        if ("DISTINCT".equals(type)) {
            return new Condition()
                    .setSql("""
                            SELECT DISTINCT
                                id,
                                create_by,
                                update_by,
                                create_time,
                                update_time,
                                %s
                            FROM
                                %s%s%s%s%s""".formatted(
                            selectList.isEmpty()
                                    ? Stream.of(clazz.getDeclaredFields())
                                    .map(f -> StrUtil.toUnderlineCase(f.getName()))
                                    .collect(Collectors.joining(",\n    "))
                                    : String.join(",", selectList),
                            tableName,
                            (StringUtils.isEmpty(sql) ? "" : "\nWHERE\n    " + sql),
                            groupList.isEmpty()
                                    ? ""
                                    : "\nGROUP BY\n    " + String.join(",\n    ", groupList),
                            orderList.isEmpty()
                                    ? "\nORDER BY\n    id " + (LocalTool.getPage().isDesc() ? "DESC" : "ASC")
                                    : "\nORDER BY\n    " + String.join(",\n    ", orderList),
                            limit == null ? "" : "\nLIMIT " + limit
                    ))
                    .setValueList(valueList);
        }
        if ("SELECT".equals(type)) {
            return new Condition()
                    .setSql("""
                            SELECT
                                id,
                                create_by,
                                update_by,
                                create_time,
                                update_time,
                                %s
                            FROM
                                %s%s%s%s%s""".formatted(
                            selectList.isEmpty()
                                    ? Stream.of(clazz.getDeclaredFields())
                                    .map(f -> StrUtil.toUnderlineCase(f.getName()))
                                    .collect(Collectors.joining(",\n    "))
                                    : String.join(",", selectList),
                            tableName,
                            (StringUtils.isEmpty(sql) ? "" : "\nWHERE\n    " + sql),
                            groupList.isEmpty()
                                    ? ""
                                    : "\nGROUP BY\n    " + String.join(",\n    ", groupList),
                            orderList.isEmpty()
                                    ? "\nORDER BY\n    id " + (LocalTool.getPage().isDesc() ? "DESC" : "ASC")
                                    : "\nORDER BY\n    " + String.join(",\n    ", orderList),
                            limit == null ? "" : "\nLIMIT " + limit
                    ))
                    .setValueList(valueList);
        }
        if ("COUNT".equals(type)) {
            return new Condition()
                    .setSql("""
                            SELECT
                                count(id)
                            FROM
                                %s%s%s%s%s""".formatted(
                            tableName,
                            (StringUtils.isEmpty(sql) ? "" : "\nWHERE\n    " + sql),
                            groupList.isEmpty()
                                    ? ""
                                    : "\nGROUP BY\n    " + String.join(",\n    ", groupList),
                            orderList.isEmpty()
                                    ? "\nORDER BY\n    id " + (LocalTool.getPage().isDesc() ? "DESC" : "ASC")
                                    : "\nORDER BY\n    " + String.join(",\n    ", orderList),
                            limit == null ? "" : "\nLIMIT " + limit
                    ))
                    .setValueList(valueList);
        }
        if ("UPDATE".equals(type)) {
            updateValueList.addAll(valueList);
            return new Condition()
                    .setSql("UPDATE\n    %s\nSET\n    %s%s%s".formatted(
                            tableName,
                                    String.join(",\n    ", updateSqlList),
                                    StringUtils.isEmpty(sql) ? "" : "\nWHERE\n    " + sql,
                                    limit == null ? "" : "\nLIMIT " + limit
                            )
                    )
                    .setValueList(updateValueList);
        }
        if ("DELETE".equals(type)) {
            return new Condition()
                    .setSql("DELETE FROM\n    %s%s%s".formatted(
                            tableName,
                                    StringUtils.isEmpty(sql) ? "" : "\nWHERE\n    " + sql,
                                    limit == null ? "" : "\nLIMIT " + limit
                            )
                    )
                    .setValueList(valueList);
        }
        throw new RuntimeException("不支持的类型");
    }

    public void addSelect(String select) {
        selectList.add(select);
    }

    public void addSelect(String columnName, String fieldName) {
        selectList.add("%s AS %s".formatted(columnName, fieldName));
    }

    public void addGroup(String group) {
        groupList.add(group);
    }

    public void addOrder(String order) {
        orderList.add(order);
    }

    public void addUpdateSql(String sql) {
        updateSqlList.add(sql);
    }

    public void addUpdateSql(String sql, String value) {
        if (value == null) return;
        updateSqlList.add(sql);
        updateValueList.add(value);
    }

    public void addUpdateSql(String sql, Integer value) {
        if (value == null) return;
        updateSqlList.add(sql);
        updateValueList.add(value);
    }

    public void addUpdateSql(String sql, Long value) {
        if (value == null) return;
        updateSqlList.add(sql);
        updateValueList.add(value);
    }

    public void addUpdateSql(String sql, BigDecimal value) {
        if (value == null) return;
        updateSqlList.add(sql);
        updateValueList.add(value);
    }

    public void addUpdateSql(String sql, LocalDateTime value) {
        if (value == null) return;
        updateSqlList.add(sql);
        updateValueList.add(value);
    }

    public void addSql(String sql) {
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
    }

    public void addSql(String sql, String value) {
        if (value == null) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.add(value);
    }

    public void addSql(String sql, Integer value) {
        if (value == null) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.add(value);
    }

    public void addSql(String sql, Long value) {
        if (value == null) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.add(value);
    }

    public void addSql(String sql, BigDecimal value) {
        if (value == null) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.add(value);
    }

    public void addSql(String sql, LocalDateTime value) {
        if (value == null) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.add(value);
    }

    public void addSql(String sql, String... value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(Arrays.asList(value));
    }

    public void addSql(String sql, Integer... value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(Arrays.asList(value));
    }

    public void addSql(String sql, Long... value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(Arrays.asList(value));
    }

    public void addSql(String sql, BigDecimal... value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(Arrays.asList(value));
    }

    public void addSql(String sql, LocalDateTime... value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(Arrays.asList(value));
    }

    public void addSqlString(String sql, List<String> value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(value);
    }

    public void addSqlInteger(String sql, List<Integer> value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(value);
    }

    public void addSqlLong(String sql, List<Long> value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(value);
    }

    public void addSqlBigDecimal(String sql, List<BigDecimal> value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(value);
    }

    public void addSqlLocalDateTime(String sql, List<LocalDateTime> value) {
        if (ArrayUtil.isEmpty(value)) return;
        log.debug("addSql: {}", sql);
        sqlList.add(sql);
        valueList.addAll(value);
    }
}
